#include "Renderer.h"
#include "GlowDevice.h"

#define BUFFER_OFFSET(a) ((char*)NULL + (a))

namespace glow
{
	using namespace std;
	
	Renderer *Renderer::_inst = NULL;
	
	Renderer::Renderer()
	{
	}

	Renderer::~Renderer()
	{
	}

	void Renderer::init(float near, float far, int fov, Color3i bgColor, int swapInterval)
	{
		_size = Vector2i(GlowDevice::getInstance()->getSize().x, GlowDevice::getInstance()->getSize().y);
		_near = near;
		_far = far;
		_fov = fov;
		_bgColor = bgColor;

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(_fov, (double)_size.x/_size.y, _near, _far);
		glEnable(GL_DEPTH_TEST);
		glColor3ub(_bgColor.r, _bgColor.g, _bgColor.b);

		//glEnable(GL_CULL_FACE);
		glPolygonMode(GL_FRONT, GL_FILL);

		glfwSwapInterval(swapInterval);
	}

	void Renderer::resize(int width, int height)
	{
		_size = Vector2i(width, height);
		glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glViewport(0, 0, _size.x, _size.y);
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(_fov, (double)_size.x/_size.y, _near, _far);
	}

	bool Renderer::initExtensions()
	{
		GLenum code = glewInit();
		if(code != GLEW_OK)
		{
			cerr << "Couldn't initialize extensions : " << glewGetErrorString(code) << endl;
			return false;
		}
		else return true;
	}

	bool Renderer::checkExtension(const char *name)
	{
		if(glewIsSupported(name)) return true;
		else return false;
	}

	float Renderer::getNear() const
	{
		return _near;
	}

	float Renderer::getFar() const
	{
		return _far;
	}

	int Renderer::getFov() const
	{
		return _fov;
	}

	Vector2i Renderer::getSize() const
	{
		return _size;
	}

	Color3i Renderer::getBgColor() const
	{
		return _bgColor;
	}

	void Renderer::setViewport(int minX, int minY, int maxX, int maxY)
	{
		glViewport(minX, minY, maxX, maxY);
	}
	
	void Renderer::set2DMode()
	{
		glDisable(GL_DEPTH_TEST);
		glMatrixMode(GL_PROJECTION);
		glPushMatrix();
		glLoadIdentity();
		gluOrtho2D(0, _size.x, 0, _size.y);
		glMatrixMode(GL_MODELVIEW);
		glPushMatrix();
		glLoadIdentity();
	}

	void Renderer::end2DMode()
	{
		glMatrixMode(GL_PROJECTION);
		glPopMatrix();
		glMatrixMode(GL_MODELVIEW);
		glPopMatrix();
		glEnable(GL_DEPTH_TEST);
	}

	void Renderer::setPolygonMode(GLenum face, GLenum mode)
	{
		glPolygonMode(face, mode);
	}

	void Renderer::lookAt(Vector3d cam, Vector3d target, Vector3d axes)
	{
		gluLookAt(cam.x, cam.y, cam.z,
				  target.x, target.y, target.z,
				  axes.x, axes.y, axes.z);
	}

	void Renderer::beginScene(Color4f color)
	{
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		glClearColor(color.r, color.g, color.b, color.a);
	}

	void Renderer::endScene()
	{
		glfwSwapBuffers();
	}

	void Renderer::setMatrix(GLenum mode)
	{
		glMatrixMode(mode);
	}

	void Renderer::pushMatrix()
	{
		glPushMatrix();
	}

	void Renderer::popMatrix()
	{
		glPopMatrix();
	}

	void Renderer::scale(const Vector3d &scale)
	{
		glScaled(scale.x, scale.y, scale.z);
	}

	void Renderer::translate(const Vector3d &pos)
	{
		glTranslated(pos.x , pos.y , pos.z);
	}

	void Renderer::rotate(GLdouble angle, const Vector3d &axes)
	{
		glRotated(angle, axes.x, axes.y, axes.z);
	}
	
	Renderer *Renderer::getInstance()
	{
		if(!_inst)
			_inst = new Renderer();
		return _inst;
	}
	
	void Renderer::destroy()
	{
		if(_inst)
			delete _inst;
		_inst = NULL;
	}
}
